package com.framework.admin.service.impl;

import com.framework.admin.domain.vo.DeptUserTreeVo;
import com.framework.admin.mapper.MenuMapper;
import com.framework.admin.mapper.RoleMapper;
import com.framework.admin.mapper.SysDeptMapper;
import com.framework.admin.model.*;
import com.framework.admin.repository.*;
import com.framework.admin.service.IRoleService;
import com.framework.admin.util.TreeUtil;
import com.framework.common.api.TreeOper;
import com.framework.common.exception.BusinessException;
import com.framework.common.listener.impl.BaseEntityCreate;
import com.framework.common.util.BeanUtils;
import com.framework.common.util.StringUtils;
import com.framework.common.util.TreeUtils;
import com.framework.common.vo.TreeNode;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Service
public class RoleService implements IRoleService {
    @Autowired
    private SysRoleRepository sysRoleRepository;
    @Autowired
    private SysRoleMenuRepository sysRoleMenuRepository;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private SysUserRoleRepository sysUserRoleRepository;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private SysUserDeptRepository userDeptRepository;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Autowired
    private MenuMapper menuMapper;

    @Override
    public Page<SysRole> getRoleList(SysRole role, PageRequest pageable) {
        return roleMapper.findRoles(role, pageable);
    }

    @Override
    public List<SysRole> allList(SysRole role) {
        return roleMapper.findList(role);
    }

    @Override
    public SysRole save(SysRole role) {
        if (StringUtils.isBlank(role.getPid())) {
            throw new BusinessException("pcode 不能为空！");
        }
        return sysRoleRepository.save(role);
    }

    @Override
    public void update(SysRole role) {
        SysRole entity = sysRoleRepository.findOne(role.getId());
        BeanUtils.copyProperties(role, entity);
        sysRoleRepository.save(entity);
    }

    @Override
    public void updateRoleMenu(String id, List<String> menus) {
        sysRoleMenuRepository.deleteByRoleId(id);
        List<String> menuIds = menuMapper.findIdByCodes(menus);
        menuIds.forEach(it -> {
            SysRoleMenu sysRoleMenu = new SysRoleMenu();
            sysRoleMenu.setRoleId(id);
            sysRoleMenu.setMenuId(it);
            sysRoleMenuRepository.save(sysRoleMenu);
        });
    }

    @Override
    public List<String> getMenuCodes(SysRole role) {
        List<SysMenu> list = menuMapper.findByRoleId(role.getId());
        return list.stream().map(SysMenu::getCode).collect(Collectors.toList());
    }

    @Override
    public void delete(List<String> ids) {
        // 获取下级
        List<SysRole> children = sysRoleRepository.findByPidIn(ids);

        for (String id : ids) {
            // 删除配置的角色和菜单
            sysRoleMenuRepository.deleteByRoleId(id);
            sysUserRoleRepository.deleteByRoleId(id);
            // 删除角色
            sysRoleRepository.deleteById(id);
        }
        // 递归删除子级
        if (children.size() > 0) {
            List<String> childrenIds = children.stream().map(BaseEntityCreate::getId).collect(Collectors.toList());
            delete(childrenIds);
        }
    }

    @Override
    public List<TreeNode<SysRole>> getTree(SysRole role) {
        List<SysRole> list = roleMapper.findList(role);

        return TreeUtils.getTree(list, new TreeOper<SysRole>() {
            @Override
            public String getItem(SysRole o) {
                return null == o.getId() ? StringUtils.EMPTY : o.getId();
            }

            @Override
            public String getParent(SysRole o) {
                if (null == o.getPid()) {
                    return StringUtils.EMPTY;
                }
                if (o.getPid().equals(role.getPid())) {
                    return "0";
                }
                return o.getPid();
            }

            @Override
            public String getId(SysRole o) {
                return o.getId();
            }

            @Override
            public String getName(SysRole o) {
                return o.getName();
            }

            @Override
            public List<SysRole> getParentList(SysRole it) {
                SysRole param = new SysRole();
                param.setId(it.getPid());
                return roleMapper.findList(param);
            }
        });
    }

    @Override
    public List<String> getRoleByUserId(String userId) {
        List<SysUserRole> list = sysUserRoleRepository.findByUserId(userId);
        return list.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
    }

    @Override
    public List<SysUser> getRoleUserInfo(String roleId) {
        List<SysUserRole> sysUserRoleList = sysUserRoleRepository.findByRoleId(roleId);
        List<SysUserRole> distinctList = Lists.newArrayList();
        if (StringUtils.isNotEmpty(sysUserRoleList)) {
            distinctList = sysUserRoleList.stream().filter(distinctByKey(SysUserRole::getUserId)).collect(Collectors.toList());
        }
        List<SysUser> roleUserInfo = Lists.newArrayList();
        for (SysUserRole sysUserRole : distinctList) {
            SysUser sysUser = userRepository.findOne(sysUserRole.getUserId());
            if (sysUser == null) {
                continue;
            }
            roleUserInfo.add(sysUser);
        }
        return roleUserInfo;
    }


    private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t -> seen.add(keyExtractor.apply(t));
    }

    /**
     * 获取部门用户树
     * @return 列表
     */
    @Override
    public List<DeptUserTreeVo> getDeptUserTree() {
        List<DeptUserTreeVo> deptTreeVos = this.getDeptTree();
        TreeUtil.treeEach(deptTreeVos, DeptUserTreeVo.class, treeOut -> {
            treeOut.setChildren(getUserByDeptId(treeOut.getId()));
            return treeOut;
        });
        return deptTreeVos;
    }

    @Override
    public void bindUserRole(String roleId, List<String> userIds) {
        if (StringUtils.isNotEmpty(userIds)) {
            for (String userId : userIds) {
                SysUserRole dbEntity = sysUserRoleRepository.findTopByRoleIdAndUserId(roleId, userId);
                if (dbEntity != null) {
                    continue;
                }
                SysUserRole userRole = new SysUserRole();
                userRole.setRoleId(roleId);
                userRole.setUserId(userId);
                sysUserRoleRepository.save(userRole);
            }
        }
    }

    @Override
    public void unbindUserRole(String roleId, List<String> userIds) {
        if (StringUtils.isNotEmpty(userIds)) {
            for (String userId : userIds) {
                List<SysUserRole> dbEntityList = sysUserRoleRepository.findByRoleIdAndUserId(roleId, userId);
                if (StringUtils.isNotEmpty(dbEntityList)) {
                    sysUserRoleRepository.deleteAll(dbEntityList);
                }
            }
        }
    }

    @Override
    public List<Map<String, String>> getTreeInfoByRoleId(String roleId) {
        List<SysUserRole> sysUserRoleList = sysUserRoleRepository.findByRoleId(roleId);
        List<String> userIds = Lists.newArrayList();
        if (StringUtils.isNotEmpty(sysUserRoleList)) {
            userIds = sysUserRoleList.stream().filter(distinctByKey(SysUserRole::getUserId))
                    .map(SysUserRole::getUserId).collect(Collectors.toList());
        }
        List<Map<String, String>> mapList = Lists.newArrayList();
        for (String userId : userIds) {
            Map<String, String> map = new HashMap<>(16);
            map.put("id", userId);
            map.put("name", userRepository.getNameById(userId));
            mapList.add(map);
        }
        return mapList;
    }

    private List<DeptUserTreeVo> getUserByDeptId(String deptId) {
        List<SysUserDept> userDepts = userDeptRepository.findByDeptId(deptId);
        List<SysUserDept> distinctList = Lists.newArrayList();
        if (StringUtils.isNotEmpty(userDepts)) {
            distinctList = userDepts.stream().filter(distinctByKey(SysUserDept::getUserId)).collect(Collectors.toList());
        }
        List<DeptUserTreeVo> deptUserTreeVos = Lists.newArrayList();
        for (SysUserDept sysUserDept : distinctList) {
            DeptUserTreeVo deptUserTreeVo = new DeptUserTreeVo();
            deptUserTreeVo.setId(sysUserDept.getUserId());
            deptUserTreeVo.setName(userRepository.getNameById(sysUserDept.getUserId()));
            deptUserTreeVos.add(deptUserTreeVo);
        }
        return deptUserTreeVos;
    }

    private List<DeptUserTreeVo> getDeptTree() {
        List<DeptUserTreeVo> topList = sysDeptMapper.getTopList();
        List<DeptUserTreeVo> childList = sysDeptMapper.getChildList();
        if (childList.size() > 0) {
            //过滤条件set，指定set预期大小为非顶级类目集合的size
            Set<String> set = Sets.newHashSetWithExpectedSize(childList.size());
            //循环顶级类目插入子类目
            topList.forEach(topVo -> getDeptTreeChild(topVo, childList, set));
            return topList;
        }
        return topList;
    }

    private static void getDeptTreeChild(DeptUserTreeVo topVo, List<DeptUserTreeVo> finalChildList, Set<String> set) {
        List<DeptUserTreeVo> childList = Lists.newArrayList();
        finalChildList.stream()
                //判断是否已循环过当前对象
                .filter(c -> !set.contains(c.getId()))
                //判断是否父子关系
                .filter(c -> c.getPid().equals(topVo.getId()))
                //set集合大小不超过finalChildList的大小
                .filter(c -> set.size() <= finalChildList.size())
                .forEach(c -> {
                    //放入set，递归循环时可以跳过这个子类目，提高循环效率
                    set.add(c.getId());
                    c.setTreeLevel(c.getTreeLevel() + 1);
                    //获取当前类目的子类目
                    getDeptTreeChild(c, finalChildList, set);
                    //加入子类目集合
                    childList.add(c);
                });
        topVo.setChildren(childList);
    }

}
